<template>
  <div class="app-container">
    <!-- 侧边栏 -->
    <div :class="['sidebar', { collapsed: isSidebarCollapsed }]">
      <div class="sidebar-header">
        <div class="logo-section" v-show="!isSidebarCollapsed">
          <img src="@/assets/logo.png" alt="无界小智" class="logo-img" />
          <span class="logo-text">无界小智</span>
        </div>
        <el-button
          :class="['collapse-button', { collapsed: isSidebarCollapsed }]"
          type="primary"
          link
          @click="toggleSidebar"
        >
          <el-icon>
            <Fold v-if="!isSidebarCollapsed" />
            <Expand v-else />
          </el-icon>
        </el-button>
      </div>
      <el-button
        class="new-chat-button"
        @click="newChat"
        :class="{ collapsed: isSidebarCollapsed }"
      >
        <template v-if="!isSidebarCollapsed">
          <el-icon><Plus /></el-icon>
          <span>新会话</span>
        </template>
        <template v-else>
          <span class="flex items-center">
            <Plus class="mr-1" />
            新对话
          </span>
        </template>
      </el-button>
    </div>

    <!-- 主要内容区 -->
    <div class="main-content">
      <div class="chat-container">
        <!-- 消息列表 -->
        <div class="message-list" ref="messaggListRef">
          <div
            v-for="(message, index) in messages"
            :key="index"
            :class="['message', message.isUser ? 'user-message' : 'bot-message']"
          >
            <el-icon :size="20" class="message-icon">
              <User v-if="message.isUser" />
              <el-icon><Service /></el-icon>
            </el-icon>
            <div class="message-content">
              <span v-html="message.content"></span>
              <span v-if="message.isThinking || message.isTyping" class="loading-dots">
                <span class="dot"></span>
                <span class="dot"></span>
              </span>
            </div>
          </div>
        </div>

        <!-- 输入区域 -->
        <div class="input-container">
          <el-input
            v-model="inputMessage"
            placeholder="请输入消息"
            :disabled="isSending"
            @keyup.enter="sendMessage"
          />
          <el-button
            type="primary"
            :loading="isSending"
            :disabled="!inputMessage.trim()"
            @click="sendMessage"
          >
            发送
          </el-button>
        </div>
      </div>
    </div>
  </div>
</template>




<script setup lang="ts">
import { onMounted, ref, watch, nextTick } from "vue";
import axios from "axios";
import { v4 as uuidv4 } from "uuid";
import { useRouter, useRoute } from "vue-router";
import debounce from "lodash-es/debounce";
import { Fold, Expand } from "@element-plus/icons-vue";
import { marked } from "marked";
// 定义组件选项

// 类型定义
interface Message {
  isUser: boolean;
  content: string;
  isTyping: boolean;
  isThinking: boolean;
}

// 响应式状态
const messaggListRef = ref<HTMLElement | null>(null);
const isSending = ref(false);
const uuid = ref<string>("");
const inputMessage = ref("");
const messages = ref<Message[]>([]);
const router = useRouter();
const route = useRoute();

// 侧边栏状态
const isSidebarCollapsed = ref(false);

// 切换侧边栏状态
const toggleSidebar = () => {
  isSidebarCollapsed.value = !isSidebarCollapsed.value;
};

// 生命周期钩子
onMounted(() => {
  initUUID();
  watch(messages, () => scrollToBottom(), { deep: true });
  hello();
});

// 消息处理方法
const scrollToBottom = () => {
  if (messaggListRef.value) {
    messaggListRef.value.scrollTop = messaggListRef.value.scrollHeight;
  }
};

const hello = () => {
  sendRequest("你好");
};

const sendMessage = () => {
  if (inputMessage.value.trim()) {
    sendRequest(inputMessage.value.trim());
    inputMessage.value = "";
  }
};

// 防抖滚动
const debouncedScroll = debounce(() => {
  scrollToBottom();
}, 100);

// API 请求方法
const sendRequest = async (message: string) => {
  try {
    isSending.value = true;

    // 添加用户消息
    const userMsg: Message = {
      isUser: true,
      content: message,
      isTyping: false,
      isThinking: false,
    };

    if (messages.value.length > 0) {
      messages.value.push(userMsg);
    }

    // 添加机器人消息
    const botMsg: Message = {
      isUser: false,
      content: "",
      isTyping: true,
      isThinking: false,
    };
    messages.value.push(botMsg);

    let accumulatedText = ""; // 用于累积完整的响应

    await axios.post(
      "/dev-api/api/v1/xiaozhi/chat",
      { memoryId: uuid.value, message },
      {
        responseType: "stream",
        onDownloadProgress: (e: any) => {
          const fullText = e.event.target.responseText;
          // 获取新的文本内容
          let newText = fullText.substring(accumulatedText.length);
          accumulatedText = fullText; // 更新累积的文本

          // 更新最后一条消息的内容
          const lastMsg = messages.value[messages.value.length - 1];
          lastMsg.content = convertStreamOutput(accumulatedText); // 使用完整的文本进行转换
          debouncedScroll();
        },
      }
    );

    messages.value[messages.value.length - 1].isTyping = false;
    isSending.value = false;
  } catch (error) {
    console.error("流式错误:", error);
    messages.value[messages.value.length - 1].content = "请求失败，请重试";
    messages.value[messages.value.length - 1].isTyping = false;
    isSending.value = false;
  }
};

// UUID 相关方法
const initUUID = () => {
  let storedUUID = localStorage.getItem("memoryId");
  if (!storedUUID) {
    storedUUID = uuidToNumber(uuidv4());
    localStorage.setItem("memoryId", storedUUID);
  }
  uuid.value = storedUUID;
};

const uuidToNumber = (uuid: string): string => {
  let number = 0;
  for (let i = 0; i < uuid.length && i < 6; i++) {
    const hexValue = uuid[i];
    number = number * 16 + (parseInt(hexValue, 16) || 0);
  }
  return (number % 1000000).toString();
};

// 工具方法
const convertStreamOutput = (output: string): string => {
  return (
    output
      // 处理换行
      .replace(/\n/g, "<br>")
      // 处理制表符
      .replace(/\t/g, "&nbsp;&nbsp;&nbsp;&nbsp;")
      // 处理Markdown列表
      .replace(/^- /gm, "• ")
      // 处理粗体
      .replace(/\*\*(.*?)\*\*/g, "<strong>$1</strong>")
      // 处理斜体
      .replace(/\*(.*?)\*/g, "<em>$1</em>")
  );
};

// 会话管理方法
const newChat = () => {
  localStorage.removeItem("memoryId");
  nextTick(() => {
    router.replace("/redirect" + route.fullPath);
  });
};
</script>


<style lang="scss" scoped>
.app-container {
  @apply flex min-h-[calc(100vh-120px)];
}

.sidebar {
  @apply w-200px bg-page flex flex-col items-center border-r border-light transition-all duration-300 relative;

  &.collapsed {
    @apply w-auto flex-row items-start border-none;

    .sidebar-header {
      @apply p-0 w-auto;
    }

    .collapse-button {
      @apply h-8 w-8 flex items-center justify-center rounded-md hover:bg-gray-100 ml-2 mt-2;
    }

    .new-chat-button {
      @apply h-8 px-3 ml-1 mt-2 rounded-full border border-primary text-primary bg-white hover:bg-primary hover:text-white transition-colors duration-200;
    }
  }

  .sidebar-header {
    @apply w-full flex flex-col items-center justify-center relative py-20px;
  }

  .logo-section {
    @apply flex flex-col items-center transition-all duration-300;
  }

  .logo-img {
    @apply w-100px h-100px;
  }

  .logo-text {
    @apply text-18px font-bold mt-10px;
  }

  .collapse-button {
    @apply absolute right-0 top-0 mr-2 mt-2;

    &.collapsed {
      @apply static m-0;
    }
  }

  .new-chat-button {
    @apply w-[calc(100%-40px)] mt-20px transition-all duration-300;

    &.collapsed {
      @apply w-auto mt-0 text-sm;
    }
  }
}

.main-content {
  @apply flex-1 p-20px overflow-y-auto;
}

.chat-container {
  @apply flex flex-col h-[calc(100vh-180px)];

  .message-list {
    @apply flex-1 overflow-y-auto p-10px border border-solid border-light rounded-4px bg-page mb-10px flex flex-col;
  }

  .message {
    @apply mb-10px p-10px rounded-4px flex gap-10px;

    &.user-message {
      @apply max-w-70% bg-primary-light-9 self-end flex-row-reverse;
    }

    &.bot-message {
      @apply max-w-full bg-success-light-9 self-start;
    }
  }

  .message-icon {
    @apply mx-10px text-1.2em;
  }

  .message-content {
    @apply flex-1;
  }

  .loading-dots {
    @apply pl-5px inline-flex items-center;

    .dot {
      @apply inline-block ml-5px w-8px h-8px bg-text-regular rounded-full;
      animation: pulse 1.2s infinite ease-in-out both;

      &:nth-child(2) {
        animation-delay: -0.6s;
      }
    }
  }
}

.input-container {
  @apply flex gap-10px;

  :deep(.el-input) {
    @apply flex-1;
  }
}

@keyframes pulse {
  0%,
  100% {
    transform: scale(0.6);
    opacity: 0.4;
  }

  50% {
    transform: scale(1);
    opacity: 1;
  }
}

// 响应式布局
@media (max-width: 768px) {
  .app-container {
    @apply flex-col;
  }

  .sidebar {
    @apply w-full flex-row justify-between items-center p-10px;

    &.collapsed {
      @apply w-auto;
    }

    .logo-section {
      @apply flex-row items-center gap-10px;
    }

    .logo-img {
      @apply w-40px h-40px;
    }

    .logo-text {
      @apply text-20px mt-0;
    }

    .new-chat-button {
      @apply m-0 mr-30px w-auto;

      &.collapsed {
        @apply w-auto mx-10px;
      }
    }
  }

  .main-content {
    @apply p-10px;
  }
}

@media (min-width: 769px) {
  .main-content {
    @apply p-0 pb-10px pl-10px;
  }
}
</style> 